#!groovy

@Library('SovrinHelpers') _

try {
    testing()
    publishing()
    if (acceptanceTesting()) {
        releasing()
    }
    notifyingSuccess()
} catch (err) {
    notifyingFailure()
    throw err
}

def testing() {
    stage('Testing') {
        parallel([
                'ubuntu-test' : { ubuntuTesting() },
                'windows-test': { windowsTesting() }
        ])
    }
}

def publishing() {
    stage('Publishing') {
        echo "${env.BRANCH_NAME}: start publishing"

        publishedVersions = parallel([
                'ubuntu-files' : { ubuntuPublishing() },
                'windows-files': { windowsPublishing() },
        ])

        if (publishedVersions['windows-files'] != publishedVersions['ubuntu-files']) {
            error "platforms artifacts have different versions"
        }
    }
}

def acceptanceTesting() {
    stage('Acceptance testing') {
        if (env.BRANCH_NAME == 'rc') {
            echo "${env.BRANCH_NAME}: acceptance testing"
            if (approval.check("default")) {
                return true
            }
        } else {
            echo "${env.BRANCH_NAME}: skip acceptance testing"
        }
        return false
    }
}

def releasing() {
    stage('Releasing') {
        if (env.BRANCH_NAME == 'rc') {
            publishingRCtoStable()
        }
    }
}

def notifyingSuccess() {
    currentBuild.result = "SUCCESS"
    node('ubuntu-master') {
        sendNotification.success('indy-crypto')
    }
}

def notifyingFailure() {
    currentBuild.result = "FAILED"
    node('ubuntu-master') {
        sendNotification.fail([slack: true])
    }
}

void getSrcVersion() {
    commit = sh(returnStdout: true, script: 'git rev-parse HEAD').trim()
    version = sh(returnStdout: true, script: "wget -q https://raw.githubusercontent.com/hyperledger/indy-crypto/$commit/libindy-crypto/Cargo.toml -O - | grep -E '^version =' | head -n1 | cut -f2 -d= | cut -f2 -d '\"'").trim()
    return version
}

def linuxTesting(file, env_name) {
    try {
        echo "${env_name} Test: Checkout csm"
        checkout scm

        def testEnv

        dir('libindy-crypto') {
            echo "${env_name} Test: Build docker image"

            testEnv = dockerHelpers.build('libindy-crypto', file)
            testEnv.inside {
                echo "${env_name} Test: Test"
                try {
                    echo "${env_name} Test: Build"
                    sh "RUST_BACKTRACE=1 cargo test --release --no-run"

                    echo "${env_name} Test: Run tests"
                    sh "RUST_BACKTRACE=1 RUST_LOG=trace cargo test --release"

                    stash includes: 'target/release/libindy_crypto.so,target/release/libindy_crypto.a', name: 'LibindyCryptoUbuntuBuildResult'
                }
                finally {
                    /* TODO FIXME restore after xunit will be fixed
                    junit 'test-results.xml'
                    */
                }
            }
        }

        sh "cp libindy-crypto/target/release/libindy_crypto.so wrappers/python"
        dir('wrappers/python') {
            testEnv.inside() {
                echo "${env_name} Test: Test python wrapper"

                sh '''
                    python3.5 -m pip install --user -e .
                    LD_LIBRARY_PATH=./ RUST_LOG=trace python3.5 -m pytest
                '''
            }
        }
    }
    finally {
        step([$class: 'WsCleanup'])
    }
}

def windowsTesting() {
    node('win2016') {
        stage('Windows Test') {
            def ws_path = "workspace/${env.JOB_NAME}".replace(' ', '_')
            ws(ws_path) {
                try {
                    echo "Windows Test: Checkout scm"
                    checkout scm

                    setupRust()

                    dir('libindy-crypto') {
                        echo "Windows Test: Download prebuilt dependencies"
                        bat 'wget -O prebuilt.zip "https://repo.sovrin.org/windows/libindy_crypto/deps/indy-crypto-deps.zip"'
                        bat 'unzip prebuilt.zip -d prebuilt'

                        echo "Windows Test: Build"
                        withEnv([
                                "OPENSSL_DIR=$WORKSPACE\\libindy-crypto\\prebuilt",
                                "PATH=$WORKSPACE\\libindy-crypto\\prebuilt\\lib;$PATH",
                                "RUST_BACKTRACE=1"
                        ]) {
                            bat "cargo test --release --no-run"

                            echo "Windows Test: Run tests"
                            withEnv(["RUST_LOG=trace"]) {
                                bat "cargo test --release"
                            }
                        }
                        stash includes: 'target/release/*.dll;prebuilt/lib/*.dll', name: 'LibindyCryptoWindowsBuildResult'
                    }

                    //TODO wrappers testing

                } finally {
                    cleanWs()
                }
            }
            cleanWs()
        }
    }
}

def ubuntuTesting() {
    node('ubuntu') {
        stage('Ubuntu Test') {
            linuxTesting("ci/ubuntu.dockerfile ci", "Ubuntu")
        }
    }
}

def ubuntuPublishing() {
    node('ubuntu') {
        stage('Publish Ubuntu Files') {
            try {
                echo 'Publish Ubuntu files: Checkout csm'
                checkout scm

                version = getSrcVersion()

                echo 'Publish Ubuntu files: Build docker image'
                testEnv = dockerHelpers.build('indy-crypto', 'libindy-crypto/ci/ubuntu.dockerfile libindy-crypto/ci')

                libindyCryptoDebPublishing(testEnv)
                libindyCryptoCargoPublishing(testEnv, false)
                pythonWrapperPypiPublishing(testEnv, false)
                pythonWrapperDebPublishing(testEnv)
            }
            finally {
                echo 'Publish Ubuntu files: Cleanup'
                step([$class: 'WsCleanup'])
            }
        }
    }

    return version
}

def windowsPublishing() {
    node('ubuntu') {
        stage('Publish Libindy Crypto Windows Files') {
            try {
                echo 'Publish Windows files: Checkout csm'
                checkout scm

                version = getSrcVersion()

                echo 'Publish Windows files: Build docker image'
                testEnv = dockerHelpers.build('indy-crypto', 'libindy-crypto/ci/ubuntu.dockerfile libindy-crypto/ci')

                dir('libindy-crypto') {
                    windowsPublishArtifact(testEnv, "LibindyCryptoWindowsBuildResult")
                }
            }
            finally {
                echo 'Publish Windows files: Cleanup'
                step([$class: 'WsCleanup'])
            }
        }
    }
    return version
}

def windowsPublishArtifact(testEnv, stashName) {
    testEnv.inside {
        sh 'chmod -R 777 ci'

        unstash name: stashName

        withCredentials([file(credentialsId: 'SovrinRepoSSHKey', variable: 'sovrin_repo_key')]) {
            sh "./ci/libindy_crypto-win-zip-and-upload.sh $version '${sovrin_repo_key}' $env.BRANCH_NAME $env.BUILD_NUMBER ./prebuilt"
        }
    }
}

def getSuffix(isRelease, target) {
    def suffix
    if (env.BRANCH_NAME == 'master' && !isRelease) {
        suffix = "-dev-$env.BUILD_NUMBER"
    } else if (env.BRANCH_NAME == 'rc') {
        if (isRelease) {
            suffix = ""
        } else {
            suffix = "-rc-$env.BUILD_NUMBER"
        }
    } else {
        error "Publish To ${target}: invalid case: branch ${env.BRANCH_NAME}, isRelease ${isRelease}"
    }
    return suffix
}

def libindyCryptoDebPublishing(testEnv) {
    echo 'Publish Libindy-Crypto deb files to Apt'

    dir('libindy-crypto/sovrin-packaging') {
        downloadPackagingUtils()
    }

    testEnv.inside {
        sh 'chmod -R 755 libindy-crypto/ci/*.sh'

        def suffix = "~$env.BUILD_NUMBER"

        unstash name: 'LibindyCryptoUbuntuBuildResult'
        sh 'mkdir --parent libindy-crypto/target/release/ && mv target/release/* libindy-crypto/target/release/'

        withCredentials([file(credentialsId: 'SovrinRepoSSHKey', variable: 'sovrin_key')]) {
            sh "cd libindy-crypto && ./ci/libindy_crypto-deb-build-and-upload.sh $version $env.BRANCH_NAME $suffix $SOVRIN_SDK_REPO_NAME $SOVRIN_REPO_HOST $sovrin_key"

            if (env.BRANCH_NAME == 'rc') {
                stash includes: 'libindy-crypto/debs/*', name: 'libindyCryptoDebs'
            }
        }
    }
}

def libindyCryptoCargoPublishing(testEnv, isRelease) {
    dir('libindy-crypto') {
        echo 'Publish Libindy-Crypto to Cargo'
        testEnv.inside {
            def suffix = getSuffix(isRelease, "Cargo")
            sh "sed -i -E -e 'H;1h;\$!d;x' -e \"s/version = \\\"([0-9,.]+)/version = \\\"\\1$suffix/\" Cargo.toml"

            withCredentials([string(credentialsId: 'cargoSecretKey', variable: 'LOGIN')]) {
                sh 'cargo login $LOGIN'
                sh 'cargo package --allow-dirty'
                sh 'cargo publish --allow-dirty'
            }
        }
    }
}

def pythonWrapperPypiPublishing(testEnv, isRelease) {
    dir('wrappers/python') {
        echo 'Publish Python Wrapper to Pypi'
        testEnv.inside {
            def suffix = getSuffix(isRelease, "Pypi")

            withCredentials([file(credentialsId: 'pypi_credentials', variable: 'credentialsFile')]) {
                sh 'cp $credentialsFile ./'
                sh "sed -i -E \"s/version='([0-9,.]+).*/version='\\1$suffix',/\" setup.py"
                sh '''
                    python3.5 setup.py sdist
                    python3.5 -m twine upload dist/* --config-file .pypirc
                '''
            }
        }
    }
}

def pythonWrapperDebPublishing(testEnv) {
    dir('wrappers/python') {
        echo 'Publish Python Wrapper deb files to APt'

        dir('sovrin-packaging') {
            downloadPackagingUtils()
        }

        testEnv.inside {
            sh 'chmod -R 755 ci/*.sh'

            def suffix = "~$env.BUILD_NUMBER"

            withCredentials([file(credentialsId: 'SovrinRepoSSHKey', variable: 'sovrin_key')]) {
                sh "./ci/python-wrapper-deb-build-and-upload.sh $env.BRANCH_NAME $suffix $SOVRIN_SDK_REPO_NAME $SOVRIN_REPO_HOST $sovrin_key"

                if (env.BRANCH_NAME == 'rc') {
                    stash includes: 'debs/*', name: 'PythonWrapperDebs'
                }
            }
        }
    }
}

def publishingRCtoStable() {
    node('ubuntu') {
        stage('Moving RC artifacts to Stable') {
            try {
                echo 'Moving RC artifacts to Stable: Checkout csm'
                checkout scm

                echo 'Moving RC artifacts to Stable: Download packaging utils'
                dir('sovrin-packaging') {
                    downloadPackagingUtils()
                }

                version = getSrcVersion()

                echo 'Moving Windows RC artifacts to Stable: libindy-crypto'
                publishLibindyCryptoWindowsFilesRCtoStable(version)

                echo 'Moving RC artifacts to Stable: Build docker image'
                testEnv = dockerHelpers.build('indy-crypto', 'libindy-crypto/ci/ubuntu.dockerfile libindy-crypto/ci')

                echo 'Moving Ubuntu RC artifacts to Stable: libindy-crypto'
                publishLibindyCryptoDebRCtoStable(testEnv, version)

                echo 'Moving RC artifacts to Stable: libindy-crypto to Cargo '
                libindyCryptoCargoPublishing(testEnv, true)

                echo 'Moving RC artifacts to Stable: python wrapper to Pypi '
                pythonWrapperPypiPublishing(testEnv, true)

                echo 'Moving RC artifacts to Stable: python wrapper deb files '
                publishPythonWrapperDebRCtoStable(testEnv, version)
            } finally {
                echo 'Moving RC artifacts to Stable: Cleanup'
                step([$class: 'WsCleanup'])
            }
        }
    }
}

def publishLibindyCryptoWindowsFilesRCtoStable(version) {
    rcFullVersion = "${version}-${env.BUILD_NUMBER}"
    withCredentials([file(credentialsId: 'SovrinRepoSSHKey', variable: 'sovrin_repo_key')]) {
        src = "/var/repository/repos/windows/libindy_crypto/rc/$rcFullVersion/"
        target = "/var/repository/repos/windows/libindy_crypto/stable/$version"

        sh "ssh -v -oStrictHostKeyChecking=no -i '$sovrin_repo_key' repo@192.168.11.115 '! ls $target'"
        sh "ssh -v -oStrictHostKeyChecking=no -i '$sovrin_repo_key' repo@192.168.11.115 cp -r $src $target"
    }
}

def publishLibindyCryptoDebRCtoStable(testEnv, version) {
    testEnv.inside {
        rcFullVersion = "${version}~${env.BUILD_NUMBER}"

        unstash name: 'libindyCryptoDebs'

        sh "fakeroot deb-reversion -v $version libindy-crypto/debs/libindy-crypto_\"$rcFullVersion\"_amd64.deb"
        sh "fakeroot deb-reversion -v $version libindy-crypto/debs/libindy-crypto-dev_\"$rcFullVersion\"_amd64.deb"

        uploadDebianFilesToStable()
    }
}

def publishPythonWrapperDebRCtoStable(testEnv, version) {
    testEnv.inside {
        rcFullVersion = "${version}~${env.BUILD_NUMBER}"

        unstash name: 'PythonWrapperDebs'
        sh "fakeroot deb-reversion -v $version debs/python3-indy-crypto_\"$rcFullVersion\"_amd64.deb"

        uploadDebianFilesToStable()
    }
}

def uploadDebianFilesToStable() {
    withCredentials([file(credentialsId: 'SovrinRepoSSHKey', variable: 'sovrin_key')]) {
        path = sh(returnStdout: true, script: 'pwd').trim()

        sh "./sovrin-packaging/upload_debs.py $path $SOVRIN_SDK_REPO_NAME stable --host $SOVRIN_REPO_HOST --ssh-key $sovrin_key"
    }
}

def downloadPackagingUtils() {
    git branch: 'master', credentialsId: 'evernym-github-machine-user', url: 'https://github.com/evernym/sovrin-packaging'
}

def shell(command) {
    if (isUnix()) {
        sh command
    } else {
        bat command
    }
}

def setupRust() {
    shell("rustup default 1.25.0")
}
